package com.carleasoft.mps.core.syslog;

import com.carleasoft.mps.core.utils.IpUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @program: prepose-manage
 * @description: aspect日志打印
 * @author: ztm
 * @create: 2019-11-12 17:14
 **/
@Aspect  // 使用@Aspect注解声明一个切面
@Component
@Slf4j
public class SysLogAspect {

    @Autowired
    private SysLogService sysLogService;

    /**
     * 这里我们使用注解的形式
     * 当然，我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
     * com.carleasoft.mps..*Controller.*(..)))
     */
    @Pointcut("execution(* com.carleasoft.mps..*Controller.*(..)))")
    public void logPointCut() {
    }


    /**
     * 环绕通知 @Around  ， 也可以使用 @Before (前置通知)  @After (后置通知)
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {


        saveBeforLog(point);

        Object result = point.proceed();
        //在方法执行过程中异常，则日志不会被记录，会被全局异常拦截
        saveAfterLog(point,result);


        return result;
    }

    private void saveBeforLog(ProceedingJoinPoint joinPoint){
        //请求的参数
        SysLogBO sysLogBO=this.getSysLogBO(joinPoint);
        String exeuTime =new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
        sysLogBO.setExeuTime(exeuTime);
        Object[] args = joinPoint.getArgs();
        List<String> list = new ArrayList<String>();
        for (Object o : args) {
            if (o instanceof HttpServletRequest || o instanceof HttpServletResponse) {
                continue;
            }
            if (o instanceof MultipartFile || o instanceof MultipartFile[]) {
                continue;
            }
            try {
                list.add(new ObjectMapper().writeValueAsString(o));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }

        sysLogBO.setParams(list.toString());

        sysLogService.save(sysLogBO,"LOG_BEFORRE");

    }

    private void saveAfterLog(ProceedingJoinPoint joinPoint,Object result){
        //请求的参数
        SysLogBO sysLogBO=this.getSysLogBO(joinPoint);
        try {
            sysLogBO.setResult(new ObjectMapper().writeValueAsString(result));
        }catch (JsonProcessingException e){
            e.printStackTrace();
        }
        sysLogService.save(sysLogBO,"LOG_AFTER");

    }


    /**
     * 解析日志
     *
     * @param joinPoint
     */
    private SysLogBO getSysLogBO(ProceedingJoinPoint joinPoint) {

        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        String remoteIp = IpUtils.getIpAddr(request);
        String uri = request.getRequestURI();

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();

        SysLogBO sysLogBO = new SysLogBO();

        sysLogBO.setRemoteIp(remoteIp);
        sysLogBO.setRequestSessionId(request.getRequestedSessionId());
        sysLogBO.setUri(uri);
        sysLogBO.setClassName(className);
        sysLogBO.setMethodName(methodName);

        return  sysLogBO;
    }
}
